/**
 * @Copyright:Copyright (c) 2007 - 2023
 */
package com.tengtong.commons.lang;

import java.util.Collection;

import com.tengtong.commons.collections.CollectionUtils;

/**
 * 字符串类
 *
 * @author lin_jinming
 * @since: v1.0
 */
public final class StringUtils {

    /**
     * 字符串为 null 或 长度为 0 时，返回 true，否则返回 false
     *
     * @param s
     * @return
     */
    public static boolean isNullOrEmpty(String s) {
        return s == null || s.length() == 0;
    }
    
    /**
     * 不是 null 并且长度不为 0时返回 true
     *
     * @param s
     * @return
     */
    public static boolean notEmpty(String s) {
        return !isNullOrEmpty(s);
    }
    
    /**
     * 如果两个字符串相等（不包括 null）则返回 true，否则返回 false
     *
     * @param s1
     * @param s2
     * @return
     */
    public static boolean equals(String s1, String s2) {
        if(s1 == null || s2 == null) {
            return false;
        }
        
        if(s1.length() != s2.length()) {
            return false;
        }
        if(s1 == s2) {
            return true;
        }
        return s1.equals(s2);
    }
    
    /**
     * 忽略大小写比较两个字符串是否相等
     *
     * @param s1
     * @param s2
     * @return
     */
    public static boolean equalsIgnoreCase(final String s1, final String s2) {
        
        if(s1 == s2) {
            return true;
        }
        
        if(s1 == null || s2 == null) {
            return false;
        }
        
        return s1.equalsIgnoreCase(s2);
    }

    /**
     * 字符串有内容（非null,非空白），包含对 utf-8 空格，空白等的判断
     *
     * @param str
     * @return
     */
    public static boolean hasText(String str) {
        if(isNullOrEmpty(str)){
            return false;
        }
        
        int strLen = str.length();
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 将对象值转为字符串值
     *
     * @param v
     * @return
     */
    public static String asString(Object v) {
        if(v != null) {
            return String.valueOf(v);
        } else {
            return null;
        }
    }
    
    /**
     * 将对象值转为字符串值，如果转换失败或 v = null，则返回给定的 defaultV 值
     *
     * @param v
     * @param defaultV
     * @return
     */
    public static String tryToString(Object v, String defaultV) {
        if(v == null) {
            return defaultV;
        }
        try {
            return String.valueOf(v);
        }
        catch (Exception e) {
            return defaultV;
        }
    }
    
    /**
     * 使用后面的 objects 值按顺序替换 string 参数中的占位符“%s”。 <br>
     * 
     * <p> 如果参数过多，会忽略多余参数
     * 
     * <p> 一般来说，简单的占位符与模板，使用本方法，如果模板比较大，并且占位符众多，可以尝试使用 StringElUtils
     *
     * @param string   如果为 null，则返回 "null"，所以要留意
     * @param objects  参数不能没有，如果为 null，会输出 "null"
     * @return
     * @since 2.12.0
     */
    public static String sprintf(String string, Object...objects) {
        return sprintf(string, true, objects);
    }
    
    /**
     * 使用后面的 objects 值按顺序替换 string 参数中的占位符“%s”。 <br>
     * 
     * <p> 一般来说，简单的占位符与模板，使用本方法，如果模板比较大，并且占位符众多，可以尝试使用 StringElUtils
     *
     * @param string   如果为 null，则返回 "null"，所以要留意
     * @param term     true 如果参数过多，则抛弃多余参数，false 对于过多的参数，以 "[x,x,x]"形式加到字符串末尾
     * @param objects  参数不能没有，如果为 null，会输出 "null"
     * @return
     * @since 2.12.0
     */
    public static String sprintf(String string, boolean term, Object...objects) {
        string = String.valueOf(string);

        if (objects == null) {
            objects = new Object[] { "(Object[])null" };
        }
        else {
            for (int i = 0; i < objects.length; i++) {
                objects[i] = asString(objects[i]);
            }
        }

        StringBuilder builder = new StringBuilder(string.length() + 16 * objects.length);
        int templateStart = 0;
        int i = 0;
        while (i < objects.length) { // 以参数的数量为循环来逐个（分段）处理
            int placeholderStart = string.indexOf("%s", templateStart);
            if (placeholderStart == -1) {
                break;
            }
            builder.append(string, templateStart, placeholderStart);
            builder.append(objects[i++]);
            templateStart = placeholderStart + 2; // +2 是因为 %s 两个字符位
        }
        builder.append(string, templateStart, string.length());

        if ((i < objects.length) && !term) { //多余参数
            builder.append(" [");
            builder.append(objects[i++]);
            while (i < objects.length) {
                builder.append(", ");
                builder.append(objects[i++]);
            }
            builder.append(']');
        }

        return builder.toString();        
        
    }
    
    /**
     * 占位符为 `{}` 大括号，如果参数过多，则抛弃多余参数
     *
     * @param msg
     * @param objects
     * @return
     */
    public static String sprintfByBraces(String msg, Object...objects) {
        return sprintf(msg, "{}", true, objects);
    }
    
    /**
     * 使用后面的 objects 值按顺序替换 string 参数中的占位符 placeholder。 <br>
     * 
     * <p> 一般来说，简单的占位符与模板，使用本方法，如果模板比较大，并且占位符众多，可以尝试使用 StringElUtils
     *
     * @param string   如果为 null，则返回 "null"，所以要留意
     * @param term     true 如果参数过多，则抛弃多余参数，false 对于过多的参数，以 "[x,x,x]"形式加到字符串末尾
     * @param objects  参数不能没有，如果为 null，会输出 "null"
     * @return
     * @since 2.12.0
     */
    public static String sprintf(String string, String placeholder, boolean term, Object...objects) {
        string = String.valueOf(string);

        if (objects == null) {
            objects = new Object[] { "(Object[])null" };
        }
        else {
            for (int i = 0; i < objects.length; i++) {
                objects[i] = StringUtils.asString(objects[i]);
            }
        }

        StringBuilder builder = new StringBuilder(string.length() + 16 * objects.length);
        int templateStart = 0;
        int i = 0;
        while (i < objects.length) { // 以参数的数量为循环来逐个（分段）处理
            int placeholderStart = string.indexOf(placeholder, templateStart);
            if (placeholderStart == -1) {
                break;
            }
            builder.append(string, templateStart, placeholderStart);
            builder.append(objects[i++]);
            templateStart = placeholderStart + placeholder.length(); // +2 是因为 {} 两个字符位
        }
        builder.append(string, templateStart, string.length());

        if ((i < objects.length) && !term) { //多余参数
            builder.append(" [");
            builder.append(objects[i++]);
            while (i < objects.length) {
                builder.append(", ");
                builder.append(objects[i++]);
            }
            builder.append(']');
        }

        return builder.toString();        
        
    }
    
    /**
     * 使用逗号分拆字符串，如果 s 为空或 null 返回 null
     *
     * @param s
     * @return
     */
    public final static String[] splitByComma(String s) {
        return split(s, ",");
    }
    
    /**
     * 用空格或空白符拆分字符串
     *
     * @param source
     * @return
     */
    public final static String[] splitBySpaces(String source) {
        return split(source, "\\s+");
    }
    
    /**
     * 分拆字符串
     *
     * @param s
     * @param regx
     * @return
     */
    public final static String[] split(String s, String regx) {
        if(isNullOrEmpty(s)) {
            return null;
        }
        return s.split(regx);
    }
    
    /**
     * 将多个字符串连接成一个，如果输入的内容为 null 或空，则返回空串
     *
     * @param strings
     * @return
     */
    public final static String concat(String...strings) {
        if(ArrayUtils.isEmpty(strings)) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for(var s: strings) {
            sb.append(s);
        }
        return sb.toString();
    }
    
    /**
     * 将容器连接成字符串
     *
     * @param <T>
     * @param cs
     * @return
     */
    public final static <T> String toString(Collection<T> cs) {
        return join(cs, "");
    }
    
    /**
     * 使用 delimiter 将容器元素连接成字符串
     *
     * @param <T>
     * @param cs
     * @param delimiter
     * @return
     */
    public final static <T> String join(Collection<T> cs, String delimiter) {
        if(CollectionUtils.isEmpty(cs)) {
            return "";
        }
        
        StringBuilder sb = new StringBuilder();
        for(var c: cs) {
            if(sb.length() > 0) {
                sb.append(delimiter);
            }
            sb.append(c);
        }
        return sb.toString();
    }
}
